package com.zmx.core.server;

import com.zmx.core.dto.ChannelInfo;
import com.zmx.core.dto.HeartInfo;
import com.zmx.core.dto.RequestDataInfo;
import com.zmx.core.utils.AESUtil;
import com.zmx.core.utils.JacksonUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @Description: 服务端接受数据handler
 * @ClassName: ServerHandler
 * @Author zhaomxr
 * @Date 2021-12-14 16:19
 */
@Slf4j
@SuppressWarnings("unchecked")
public class ServerHandler extends SimpleChannelInboundHandler<ChannelInfo> {

    private String ip;
    private String username;
    private String password;
    private String secret;

    private Map<String, HeartInfo> heartInfoMap = new HashMap<String, HeartInfo>();

    private static final List<String> authList = new ArrayList<String>();

    public ServerHandler() {
    }

    public ServerHandler(String username, String password, String secret) {
        this.username = username;
        this.password = password;
        this.secret = secret;
    }

    //static {
    //    username = getValue("auth.username");
    //    password = getValue("auth.password");
    //    secret = getValue("auth.secret");
    //}

    //public static String getValue(String key) {
    //    Properties prop = new Properties();
    //    try {
    //        //装载配置文件
    //        prop.load(new FileInputStream(new File("src//application.properties")));
    //    } catch (IOException e) {
    //        e.printStackTrace();
    //    }
    //    //返回获取的值
    //    return prop.getProperty(key);
    //}

    /**
     * 认证成功返回给slave的key，一般是slave传过来的key的再加密后的东西，但是这里简单处理了
     */
    private static final String SUCCESS_KEY = "OK";

    //服务器会接收到2种消息 一个是客户端初始化时发送过来的认证信息 第二个是心跳信息
    //@Override
    //public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    //    log.info("channelRead Object:{}", msg);
    //
    //    if (msg instanceof Map) {
    //        auth(ctx, msg);
    //    } else if (msg instanceof HeartInfo) {
    //        ctx.writeAndFlush(200);
    //
    //        HeartInfo heartInfo = (HeartInfo) msg;
    //
    //        saveInfoInstance(heartInfo);
    //        log.info("心跳接收成功! {}", heartInfo);
    //        heartInfoMap.put(heartInfo.getIp() + ":" + heartInfo.getPort(), heartInfo);
    //    }
    //}

    //服务器会接收到2种消息 一个是客户端初始化时发送过来的认证信息 第二个是心跳信息
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChannelInfo channelInfo) throws Exception {
        log.info("channelRead Object:{}", channelInfo);

        if (Objects.equals(channelInfo.getType(), "auth")) {
            auth(ctx, channelInfo);
        } else if (Objects.equals(channelInfo.getType(), "heart")) {
            ChannelInfo channelInfoR = new ChannelInfo(channelInfo.getType(), SUCCESS_KEY);
            ctx.writeAndFlush(channelInfoR);

            HeartInfo heartInfo = (HeartInfo) channelInfo.getContent();

            saveInfoInstance(heartInfo);
            log.info("心跳接收成功! {}", heartInfo);
            heartInfoMap.put(heartInfo.getIp() + ":" + heartInfo.getPort(), heartInfo);
        }
    }

    private void auth(ChannelHandlerContext ctx, ChannelInfo channelInfo) throws Exception {
        Map<String, String> data = (Map) channelInfo.getContent();
        log.info("auth data: {}", data);

        //使用AES解密 用户名密码加密后字符串
        if (data != null && Objects.equals(AESUtil.aesDecrypt(data.get("secret"), secret), username + password)) {
            log.info("认证成功 客户端 [{}] 已上线！", data.get("node"));
            saveOnLineInstance(data.get("node"));

            //认证成功给slave返回信息
            ChannelInfo channelInfoR = new ChannelInfo(channelInfo.getType(), SUCCESS_KEY);
            ctx.writeAndFlush(channelInfoR);
        } else {
            //认证不成功就返回失败信息并关闭通道
            log.info("认证失败");
            ChannelInfo channelInfoR = new ChannelInfo(channelInfo.getType(), "auth failure!");
            ctx.writeAndFlush(channelInfoR);
        }
    }

    /**
     * 保存 存活节点-是否在线
     *
     * @param client
     */
    private void saveOnLineInstance(String client) {
        if (!StringUtils.isEmpty(client)) {
            ClientInstanceInfo.setOnLineInstance(client, true);
        }
    }

    /**
     * 保存 存活节点-内存/cpu等数据信息
     *
     * @param heartInfo
     */
    private void saveInfoInstance(HeartInfo heartInfo) {
        if (heartInfo != null) {
            ClientInstanceInfo.setInfoInstance(heartInfo);
        }
    }
}
